﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using System;
using System.Diagnostics.CodeAnalysis;
using RazorEngine.Compilation.CSharp;
using RazorEngine.Compilation.VisualBasic;
using System.Security;
using System.IO;
using System.Reflection;
using System.Diagnostics.Contracts;
using System.CodeDom.Compiler;
using System.Security.Principal;
using RazorEngine.Templating;
using System.Security.Permissions;
using System.Linq;
using System.Collections.Generic;
using RazorEngine.Compilation.ReferenceResolver;
using RazorEngine.Compilation;
using System.Linq.Expressions;
using RazorEngine.Configuration;
using OF.SmartMVC.Core.Compile.Template;

namespace OF.SmartMVC.Core.Compile
{
    public class EngineHelper
    {
        private static HtmlTemplateService htmlTemplateService = new HtmlTemplateService();
        private static Dictionary<string, Dictionary<string, Func<SmartTemplateBase>>> templateTypeDict = new Dictionary<string, Dictionary<string, Func<SmartTemplateBase>>>();
        private static Type BaseTemplateType = typeof(SmartTemplateBase<>);
        private static StoreDllCacheProvider StoreDllCacheProvider = new StoreDllCacheProvider();
        
        public static EngineConfig Config = new EngineConfig();
        public static IRazorEngineService CreateEngineService(Type rootType, List<Assembly> referAssemblis, string template)
        {            
            var config = new TemplateServiceConfiguration
            {
                TemplateManager = new EmbeddedResourceTemplateManager(rootType),
                CompilerServiceFactory = new StoreDllCompilerServiceFactory(referAssemblis, template),
                CachingProvider = EngineHelper.StoreDllCacheProvider,
                BaseTemplateType = EngineHelper.BaseTemplateType                
            };
            IRazorEngineService razorEngineService = RazorEngineService.Create(config);
            return razorEngineService;
        }

        private static void InitDeclareFunc(RunViewRequest parentRequest, SmartTemplateBase instance,
            RunViewRequest childRequest)
        { 
            instance.DeclareFunc(childRequest.funcName, (obj) =>
            {
                var request = childRequest;//.Value;
                //request.funcName = childKV.Key;
                if (obj != null)
                {
                    request.model = obj;
                }
                request.Html = parentRequest.Html;
                request.Url = parentRequest.Url;
                return EngineHelper.RunView(request);
            });
        }

        public static string RunView(RunViewRequest request)
        {
            Func<SmartTemplateBase> func = templateTypeDict[request.assemblyName][request.viewName];
            SmartTemplateBase instance = func();
            instance.SetData(request.model, request.viewbag);
            instance.InternalTemplateService = htmlTemplateService;
            instance.FuncName = request.funcName;
            instance.Url = request.Url;
            instance.Html = request.Html;
            if (request.funcList != null)
            {
                foreach (var item in request.funcList)
                {
                    InitDeclareFunc(request, instance, item);
                }
            }
            using (StringWriter sw = new StringWriter())
            {
                ((ITemplate)instance).Run(new ExecuteContext(), sw);
                return sw.ToString();
            }
        }

        private static Func<T> CreateInstanceDelegate<T>(Type type)
        {
            NewExpression newExp = Expression.New(type);
            var resultExpression = Expression.Convert(newExp, typeof(T));
            Expression<Func<T>> lambdaExp = Expression.Lambda<Func<T>>(resultExpression, null);
            Func<T> func = lambdaExp.Compile();
            return func;
        }

        public static void LoadAssembly(string assemblyName)
        {
            Assembly assembly = Assembly.Load(assemblyName);
            var funcTypeDict = new Dictionary<string, Func<SmartTemplateBase>>();
            Type baseType = typeof(SmartTemplateBase);
            templateTypeDict.Add(assemblyName, funcTypeDict);
            foreach (var type in assembly.GetTypes())
            {
                if (type.IsNested)
                {
                    continue;
                }
                if (!baseType.IsAssignableFrom(type))
                {
                    continue;
                }
                Func<SmartTemplateBase> func = CreateInstanceDelegate<SmartTemplateBase>(type);
                funcTypeDict.Add(type.Name.Replace('_', '.'), func);
            }
        }
    }
}